/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.ove.palette;

/**
 *
 * @author Mr.Sam
 */
import java.awt.*;
import java.io.Serializable;

/**
* A layout manager for component to be laid out as a palette.<BR>
* We can specified the number max of element allowed by row or by column according to
* the constraint chosen.
* For example PaletteLayout(PaletteLayout.COLUMN_CONSTRAINT, 3) will lay a container with
* the constraint that, for the most, we can have 3 components by column.<BR>
* All components have the same size, which corresponds to the biggest "preferredWidth"
* and the biggest "preferredHeight".
*
* @author © 1999 DIRAT Laurent
* @version 1.0  17/09/1999
*/
public class PaletteLayout implements LayoutManager2, Serializable {
    /**
    * The different kinds of constraint.
    */
    
    /**
    * The specified number of elements correspond to the number max by row.
    */
    public static final byte ROW_CONSTRAINT = 0;
    
    /**
    * The specified number of elements correspond to the number max by row.
    */
    public static final byte COLUMN_CONSTRAINT = 1;
    
    /**
    * The constraint type.
    */
    private byte constraint;
    
    /**
    * The number max of element by row or by column according to the type of constraint.
    */
    private int nbElement;
    
    /**
    * The preferred size
    */
    private Dimension prefSize;
    
    /**
    * The default constructor.
    * The default is a container laid out with 3, for the most, elements by row.
    */
    public PaletteLayout() {
        this(ROW_CONSTRAINT, 3);
    }
    
    /**
    * The constructor.
    * @constraint the constraint (<CODE>ROW_CONSTRAINT</CODE> or <CODE>COLUMN_CONSTRAINT</CODE>) for ordering
    * the components in the container laid out.
    * @param nbElement the number max of element allowed by row (resp. column) with the <CODE>ROW_CONSTRAINT</CODE>
    * (resp. <CODE>COLUMN_CONSTRAINT</CODE>) constraint.
    */
    public PaletteLayout(byte constraint, int nbElement) {
        if ((constraint == ROW_CONSTRAINT) || (constraint == COLUMN_CONSTRAINT))
            this.constraint = constraint;
        else
            this.constraint = ROW_CONSTRAINT;
            
        if (nbElement > 0)
            this.nbElement = nbElement;
        else
            this.nbElement = 3;
    }
    
    /**
    * Sets the constraint (<CODE>ROW_CONSTRAINT</CODE> or <CODE>COLUMN_CONSTRAINT</CODE>) for ordering
    * the components in the container laid out.<BR>
    * After a call to this method, a call to <CODE>validate</CODE> on the container laid out should be
    * performed to see the change take effect.
    * @param constraint the constraint to set.
    */
    public void setConstraint(byte constraint) {
        if ((constraint == ROW_CONSTRAINT) || (constraint == COLUMN_CONSTRAINT))
            this.constraint = constraint;
        else
            this.constraint = ROW_CONSTRAINT;
    }
    
    /**
    * Returns the constraint put on the container laid out.
    */
    public byte getConstraint() {
        return constraint;
    }
    
    /**
    * Sets the number max of element allowed by row (resp. column) with the <CODE>ROW_CONSTRAINT</CODE>
    * (resp. <CODE>COLUMN_CONSTRAINT</CODE>) constraint.
    * After a call to this method, a call to <CODE>validate</CODE> on the container laid out should be
    * performed to see the change take effect.
    * @param nbElement the number of element allowed.
    */
    public void setNbElement(int nbElement) {
        if (nbElement > 0)
            this.nbElement = nbElement;
    }
    
    /**
    * Returns the number max of element allowed by row (resp. column) with the <CODE>ROW_CONSTRAINT</CODE>
    * (resp. <CODE>COLUMN_CONSTRAINT</CODE>) constraint.
    */
    public int getNbElement() {
        return nbElement;
    }
    
    
    // Methods of the LayoutManager interface
    
    /**
     * Adds the specified component with the specified name to
     * the layout.
     * @param name the component name
     * @param comp the component to be added
     */
    public void addLayoutComponent(String name, Component comp) {
    }
    
    /**
    * Lays out the container in the specified panel.
    * @param parent the component which needs to be laid out
    */
    public void layoutContainer(Container parent) {
        int count = parent.getComponentCount();
        int maxWidth = 0;
        int maxHeight = 0;
        Dimension dim; 
        Component comp;
        for (int i = 0; i < count; i++) {
            comp = parent.getComponent(i);
            comp.setSize(0, 0); // Sinon pb avec certains composants de l'AWT (Button notamment) :o((
            dim = comp.getPreferredSize();
            maxWidth = (int) Math.max(maxWidth, dim.width);
            maxHeight = (int) Math.max(maxHeight, dim.height);
        }
        
        Dimension size = parent.getSize();
        
        if (size == null)
            size = prefSize;
        
        int startX = 0;
        int startY = 0;
        
        if ((size != null) && (prefSize != null)) {
            startX = (size.width - prefSize.width) / 2;
            startY = (size.height - prefSize.height) / 2;
        }
        
        int shiftX = startX;
        int shiftY = startY;
        
        if (constraint == COLUMN_CONSTRAINT) {
            for (int i = 0; i < count; i++) {
                comp = parent.getComponent(i);
                comp.setBounds(shiftX, shiftY, maxWidth, maxHeight);
                
                if (((i != 0) && (((i + 1) % nbElement) == 0)) || (nbElement == 1)) {
                    shiftX = startX;
                    shiftY += maxHeight;
                }
                else
                    shiftX += maxWidth;
            }
        }
        else {
            for (int i = 0; i < count; i++) {
                comp = parent.getComponent(i);
                comp.setBounds(shiftX, shiftY, maxWidth, maxHeight);
                
                if (((i != 0) && (((i + 1) % nbElement) == 0)) || (nbElement == 1)) {
                    shiftY = startY;
                    shiftX += maxWidth;
                }
                else
                    shiftY += maxHeight;
            }
        }
        
    }

    /** 
     * Calculates the minimum size dimensions for the specified 
     * panel given the components in the specified parent container.
     * @param parent the component to be laid out
     * @see #preferredLayoutSize
     */
    public Dimension minimumLayoutSize(Container parent) {
        return preferredLayoutSize(parent);
    }
    
    /**
    * Calculates the preferred size dimensions for the specified
    * panel given the components in the specified parent container. 
    * @param parent the component to be laid out 
    */
    public Dimension preferredLayoutSize(Container parent) {
        int count = parent.getComponentCount();
        int maxWidth = 0;
        int maxHeight = 0;
        Dimension dim = new Dimension();
        
        for (int i = 0; i < count; i++) {
            dim = parent.getComponent(i).getPreferredSize();
            maxWidth = (int) Math.max(maxWidth, dim.width);
            maxHeight = (int) Math.max(maxHeight, dim.height);
        }
        
        int factor = 1;
        
        if (nbElement > 1) {
            factor = count / nbElement;
            factor = ((count % nbElement) == 0) ? factor : factor + 1;
        }
        
        if (constraint == COLUMN_CONSTRAINT) {
            if (nbElement > 1) {
                dim.width = (count < nbElement) ? count*maxWidth : nbElement*maxWidth;
                dim.height = factor*maxHeight;
            }
            else {
                dim.width = maxWidth;
                dim.height = count*maxHeight;
            }
        }
        else {
            if (nbElement > 1) {
                dim.width = factor*maxWidth;
                dim.height = (count < nbElement) ? count*maxHeight : nbElement*maxHeight;
            }
            else {
                dim.width = count*maxWidth;
                dim.height = maxHeight;
            }
        }
        
        prefSize = new Dimension(dim);
        return dim;
    }

    /**
     * Removes the specified component from the layout.
     * @param comp the component ot be removed
     */
    public void removeLayoutComponent(Component comp) {
    }

    // Methods of the LayoutManager2 interface

    /**
     * Adds the specified component to the layout, using the specified
     * constraint object.
     * @param comp the component to be added
     * @param constraints  where/how the component is added to the layout.
     */
    public void addLayoutComponent(Component comp, Object constraints) {
    }

    /** 
     * Returns the maximum size of this component.
     * @see java.awt.Component#getMinimumSize()
     * @see java.awt.Component#getPreferredSize()
     * @see LayoutManager
     */
    public Dimension maximumLayoutSize(Container target) {
        return preferredLayoutSize(target);
    }

    /**
     * Returns the alignment along the x axis.  This specifies how
     * the component would like to be aligned relative to other 
     * components.  The value should be a number between 0 and 1
     * where 0 represents alignment along the origin, 1 is aligned
     * the furthest away from the origin, 0.5 is centered, etc.
     */
    public float getLayoutAlignmentX(Container target) {
        return 0.0f;
    }

    /**
     * Returns the alignment along the y axis.  This specifies how
     * the component would like to be aligned relative to other 
     * components.  The value should be a number between 0 and 1
     * where 0 represents alignment along the origin, 1 is aligned
     * the furthest away from the origin, 0.5 is centered, etc.
     */
    public float getLayoutAlignmentY(Container target) {
        return 0.0f;
    }

    /**
     * Invalidates the layout, indicating that if the layout manager
     * has cached information it should be discarded.
     */
    public void invalidateLayout(Container target) {
    }
    
}


